﻿
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Nirvana
{
    public class AudioClipPool : CSharpSingletion<AudioClipPool>
    {
        /// <summary>
        /// 保存以AB名加载的记录，以便下次直接加载
        /// </summary>
        private Dictionary<AssetID, AudioClipCache> AudioClipCacheAssetIdMap = new Dictionary<AssetID, AudioClipCache>();
        /// <summary>
        /// 保存以AudioClip资源加载的记录，以便下次直接加载
        /// </summary>
        private Dictionary<AudioClip, AudioClipCache> AudioClipCacheObjMap = new Dictionary<AudioClip, AudioClipCache>();

        private float defaultReleaseAfterFree = 30f;
        private float curLoadTime;

        public float DefaultReleaseAfterFree
        {
            get
            {
                return defaultReleaseAfterFree;
            }
            set
            {
                defaultReleaseAfterFree = value;
                foreach (var p in AudioClipCacheAssetIdMap)
                    p.Value.SetReleaseTimeAfterFree(value);
            }
        }
        public Dictionary<AssetID, AudioClipCache> GetCacheAssetIDMap()
        {
            return AudioClipCacheAssetIdMap;
        }

  
        /// <summary>
        /// 初始化
        /// </summary>
        protected override void Init()
        {
            Scheduler.AddFrameListener(Remove);
        }

        public void Load(AssetID assetID, Action<AudioClip> complete)
        {
            Scheduler.RunCoroutine(this.LoadAudioASync(assetID, complete));
        }
        public WaitLoadAudioClip Load(AssetID assetid)
        {
            AudioClipCache cache;
            if (this.AudioClipCacheAssetIdMap.TryGetValue(assetid, out cache))
            {
                cache.Retain();
                return new WaitLoadAudioClip(cache);
            }

            cache = new AudioClipCache(assetid, this.AudioClipCacheObjMap);
            cache.SetReleaseTimeAfterFree(this.defaultReleaseAfterFree);
            cache.Load(assetid);
            cache.Retain();
            this.AudioClipCacheAssetIdMap.Add(assetid, cache);
            return new WaitLoadAudioClip(cache);
        }

        private IEnumerator LoadAudioASync(AssetID assetid, Action<AudioClip> cb)
        {
            WaitLoadAudioClip wait = this.Load(assetid);
            if (wait.keepWaiting)
            {
                yield return wait;
            }
            if (!string.IsNullOrEmpty(wait.Error))
            {
                Debug.LogError(wait.Error);
                cb(null);
            }
            else
            {
                cb(wait.LoadedObject);
            }
            yield break;
        }


        public void Free(AudioClip obj, bool destroy = false)
        {
            if (obj == null)
            {
                Debug.Log("Try to free a null AudioClip.");
                return;
            }
            AudioClipCache cache;
            if (!this.AudioClipCacheObjMap.TryGetValue(obj, out cache))
            {
                Debug.LogFormat("Try to free an instance {0} not allocated by this pool.", obj.name);
                return;
            }
            cache.Release();
            if (destroy && cache.ReferenceCount == 0)//引用计数为0的才强制清除
            {
                AssetID assetID = cache.AssetID;
                var audioclip = cache.GetObject();
                if (audioclip != null)
                {
                    AssetManager.UnloadAsseBundle(assetID.BundleName);
                    this.AudioClipCacheObjMap.Remove(audioclip);
                }
                this.AudioClipCacheAssetIdMap.Remove(assetID);
            }
        }

        public bool Retain(AudioClip obj)
        {
            if (obj == null)
            {
                Debug.Log("Try to free a null AudioClip.");
                return false;
            }
            AudioClipCache cache;
            if (!this.AudioClipCacheObjMap.TryGetValue(obj, out cache))
            {
                Debug.LogFormat("Try to free an instance {0} not allocated by this pool.", obj.name);
                return false;
            }
            cache.Retain();
            return true;
        }

        public void Clear()
        {
            this.AudioClipCacheAssetIdMap.Clear();
            this.AudioClipCacheObjMap.Clear();
        }
        public void ClearAllUnused()
        {
            this.AudioClipCacheAssetIdMap.RemoveAll(Unload);
        }


        List<AssetID> listtmp = new List<AssetID>();
        private void Remove()
        {
            //每秒检测释放未引用的资源
            if (Time.time - curLoadTime >= 1f)
            {
                this.curLoadTime = Time.time;
                listtmp.Clear();
                foreach (var keyValuePair in AudioClipCacheAssetIdMap)
                {
                    if (UnloadOnTime(keyValuePair.Key, keyValuePair.Value))
                    {
                        listtmp.Add(keyValuePair.Key);
                    }
                }
                for (int i = 0; i < listtmp.Count; i++)
                {
                    AudioClipCacheAssetIdMap.Remove(listtmp[i]);
                }
                listtmp.Clear();
            }
        }

        private bool UnloadOnTime(AssetID asset, AudioClipCache cache)
        {
            if (!string.IsNullOrEmpty(cache.Error))
            {
                return true;
            }
            if (cache.ReferenceCount != 0)
            {
                return false;
            }
            float _t = cache.LastFreeTime + cache.ReleaseAfterFree;
            if (Time.realtimeSinceStartup > _t)
            {
                var audioclip = cache.GetObject();
                if (audioclip != null)
                {
                    this.AudioClipCacheObjMap.Remove(audioclip);
                    AssetManager.UnloadAsseBundle(asset.BundleName);
                }
                return true;
            }
            return false;
        }

        private bool Unload(AssetID assetid, AudioClipCache cache)
        {
            if (!string.IsNullOrEmpty(cache.Error))
            {
                return true;
            }

            if (cache.ReferenceCount > 0)
            {
                return false;
            }

            var obj = cache.GetObject();
            if (obj != null)
            {
                this.AudioClipCacheObjMap.Remove(obj);
                AssetManager.UnloadAsseBundle(assetid.BundleName);
            }
            return true;
        }

    }
}
